
#include<iostream>
#include "rendering.h"
using namespace std;

extern Vicsek vicsek;
extern float Alpha;
extern float Beta;
extern float Gamma;
float L = vicsek.L;
static float viewx = 0, viewy = 0, viewz = 50;
static float viewlx = 0, viewly = 0, viewlz = -1;
static const float cameraMoveSpeed = 0.5;
static const float cameraRotateSpeed = 0.05;
static float viewAngle = 0, viewAngleZ = 0;
const float pi = 3.1415926;

void rendering(int argc, char** argv)
{
	//@tc : set window
	glutInit(&argc, argv);

	//@tc : set lighting
	const GLfloat light_ambient[]  = { 0.0f, 0.0f, 0.0f, 1.0f };
	const GLfloat light_diffuse[]  = { 1.0f, 1.0f, 1.0f, 1.0f };
	const GLfloat light_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
	const GLfloat light_position[] = { 0.0f, L*1.0f, L*1.0f, 0.0f };
	const GLfloat mat_ambient[]    = { 0.7f, 0.7f, 0.7f, 1.0f };
	const GLfloat mat_diffuse[]    = { 0.8f, 0.8f, 0.8f, 1.0f };
	const GLfloat mat_specular[]   = { 1.0f, 1.0f, 1.0f, 1.0f };
	const GLfloat high_shininess[] = { 100.0f };
	glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowPosition(300,50);
	glutInitWindowSize(1024, 1024);

	//@tc : the caption of the window.
	glutCreateWindow("Light Balls");

	//@tc : displays the scene.
	glutDisplayFunc(renderScene);
	glutReshapeFunc(changeAspectRatio);
	glutIdleFunc(renderScene);
	glutSpecialFunc( specialKeys );
	glutKeyboardFunc( normalKeys );
	glutMouseFunc( MouseKey );
	glutMotionFunc( MouseMove );
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LESS);

	glEnable(GL_LIGHT0);
	glEnable(GL_NORMALIZE);
	glEnable(GL_COLOR_MATERIAL);
	glEnable(GL_LIGHTING);
	glLightfv(GL_LIGHT0, GL_AMBIENT,  light_ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
	glLightfv(GL_LIGHT0, GL_POSITION, light_position);

	glMaterialfv(GL_FRONT, GL_AMBIENT,   mat_ambient);
	glMaterialfv(GL_FRONT, GL_DIFFUSE,   mat_diffuse);
	glMaterialfv(GL_FRONT, GL_SPECULAR,  mat_specular);
	glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);
	glutMainLoop();
}
void changeAspectRatio(int w, int h)
{
	if ( h == 0 ) h = 1;
	float ratio = 1.0 * w / h;

	//@tc : set projection mode
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity();

	//@tc : set the view range. w h means the whole window.
	glViewport(0, 0, w, h);


	//@tc : set aspect ratio, whenever the windows changes size, the object ramains the original aspect ratio
	gluPerspective(45, ratio, 0.1, 1000);


	//@tc : set view model
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	gluLookAt(viewx, viewy, viewz, viewx + viewlx ,viewy + viewly, viewz + viewlz, 0, 1, 0);
}

void renderScene()
{

	static int frame = 0;
	static int timeBase = 0;
	int time = glutGet( GLUT_ELAPSED_TIME );

	if (time - timeBase > 15)
	{
		vicsek.Simulate(Alpha, Beta, Gamma);
		//cout<<"\r"<<"FPS : "<<frame * 1000.0 / (time - timeBase) ;
		frame = 0;
		timeBase = time;
	}
	frame++;

	//@tc : draws the cubic
	//@tc : Front Back + Four Lines
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	//@tc :enable dash line
	glEnable(GL_LINE_STIPPLE);
	glColor3f(L, L, L);
	glLineWidth( 1 );
	//glLineStipple(L,0xFFFF);
	glPushMatrix();
	glBegin( GL_LINE_LOOP );
	glVertex3f(L, L, 0);
	glVertex3f(-L, L, 0);
	glVertex3f(-L, -L, 0);
	glVertex3f(L, -L, 0);
	glEnd();
	glPopMatrix();

	///*
	//	glLineWidth( 0.2 );
	//	glLineStipple(L, 0x0F0F);
	//	glBegin( GL_LINE_LOOP );
	//	glVertex3f(0, 0, -2*L);
	//	glVertex3f(L, 0, -2*L);
	//	glVertex3f(L, L, -2*L);
	//	glVertex3f(0, L, -2*L);
	//	glEnd();
	//
	//	glBegin( GL_LINES );
	//	glVertex3f(0, L, -L);
	//	glVertex3f(0, L, -2*L);
	//	glEnd();
	//
	//	glBegin( GL_LINES );
	//	glVertex3f(0, 0, -L);
	//	glVertex3f(0, 0, -2*L);
	//	glEnd();
	//
	//	glBegin( GL_LINES );
	//	glVertex3f(L, 0, -L);
	//	glVertex3f(L, 0, -2*L);
	//	glEnd();
	//
	//	glBegin( GL_LINES );
	//	glVertex3f(L, L, -L);
	//	glVertex3f(L, L, -2*L);
	//	glEnd();*/
	GLuint ballDL = glGenLists( 1 );
	glNewList( ballDL, GL_COMPILE );

	glutSolidSphere(0.1, 8, 8);
	glEndList();
	glColor3f(1, 0, 0);

	for(int i = 0; i < vicsek.N; i++)
	{
		//@tc : set different colors to idenfity different clusters.
		if (i == 0)
			glColor3f(0,1,0);
		else{
			glColor3f(1,0,0);
		}
		float x = (float)(vicsek.x[i]);
		float y = (float)(vicsek.y[i]);
		float z = 0;
		glPushMatrix();
		glTranslatef(x, y, z);
		glCallList( ballDL );
		glPopMatrix();

	}
	glutPostRedisplay();
	glutSwapBuffers();
}

void cameraMoveLeft (int dx)
{
	viewx = viewx + cameraMoveSpeed * dx;
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	gluLookAt(viewx, viewy, viewz, viewx + viewlx, viewy + viewly, viewz + viewlz, 0, 1, 0);
}

void cameraMoveUp( int dy )
{
	viewy = viewy + cameraMoveSpeed * dy;
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	gluLookAt(viewx, viewy, viewz, viewx + viewlx, viewy + viewly, viewz + viewlz, 0, 1, 0);
}

void cameraMoveFront (int dz)
{
	viewz = viewz + cameraMoveSpeed * dz;
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	gluLookAt(viewx, viewy, viewz, viewx + viewlx, viewy + viewly, viewz + viewlz, 0, 1, 0);
}

void cameraMoveForward (int dd)
{
	viewx += dd*cameraMoveSpeed*viewlx;
	viewy += dd*cameraMoveSpeed*viewly;
	viewz += dd*cameraMoveSpeed*viewlz;
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	gluLookAt(viewx, viewy, viewz, viewx + viewlx, viewy + viewly, viewz + viewlz, 0, 1, 0);
}

void cameraMovePlayerLeft(int dl)
{
	double vx = -viewlz;
	double vy =  viewlx;
	double ab = sqrt(vx*vx+vy*vy);
	vx /= ab;
	vy /= ab;
	viewx += dl*cameraMoveSpeed*vx;
	viewz += dl*cameraMoveSpeed*vy;
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	gluLookAt(viewx, viewy, viewz, viewx + viewlx, viewy + viewly, viewz + viewlz, 0, 1, 0);
}

void cameraRotateLeft(double dx)
{
	viewAngle += cameraRotateSpeed * dx;
	viewlx =  sin( viewAngle )*cos(viewAngleZ);
	viewlz = -cos( viewAngle )*cos(viewAngleZ);
	viewly = sin(viewAngleZ);
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	gluLookAt(viewx, viewy, viewz, viewx + viewlx, viewy + viewly, viewz + viewlz, 0, 1, 0);
}

void cameraRotateUp(double dz)
{
	viewAngleZ += cameraRotateSpeed * dz;
	if (abs(viewAngleZ) >= pi/2) {
		viewAngleZ -= cameraRotateSpeed * dz;
	}
	viewlx =  sin( viewAngle )*cos(viewAngleZ);
	viewlz = -cos( viewAngle )*cos(viewAngleZ);
	viewly = sin(viewAngleZ);
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	gluLookAt(viewx, viewy, viewz, viewx + viewlx, viewy + viewly, viewz + viewlz, 0, 1, 0);
}

void specialKeys(int key, int x, int y)
{
	switch ( key )
	{
		// coordinate movement
	case GLUT_KEY_LEFT : {
		int specialKey = glutGetModifiers();
		if (specialKey == GLUT_ACTIVE_CTRL )
			cameraRotateLeft( -1 );
		else
			cameraMoveLeft ( -1 );
		break;
						 }
	case GLUT_KEY_RIGHT : {
		int specialKey = glutGetModifiers();
		if (specialKey == GLUT_ACTIVE_CTRL )
			cameraRotateLeft( 1 );
		else
			cameraMoveLeft ( 1 );
		break;
						  }
	case GLUT_KEY_UP : cameraMoveUp( 1 ); break;
	case GLUT_KEY_DOWN : cameraMoveUp( -1 ); break;
	case GLUT_KEY_PAGE_UP : cameraMoveFront( -1 ); break;
	case GLUT_KEY_PAGE_DOWN : cameraMoveFront( 1 ); break;
	}
}

void normalKeys(unsigned char key, int x, int y)
{
	switch ( key ) {
	case 27 : exit(0); break;
		// player view movement
	case 'w' : cameraMoveForward(  1 ); break;     // forward
	case 's' : cameraMoveForward( -1 ); break;     // backward
	case 'a' : cameraMovePlayerLeft( -1 ); break;  // move left
	case 'd' : cameraMovePlayerLeft(  1 ); break;  // move right
	case 'q' : cameraRotateUp(  1 ); break;        // turn up
	case 'z' : cameraRotateUp( -1 ); break;        // turn down
	case 'e' : cameraMoveUp(  1 ); break;          // move up
	case 'c' : cameraMoveUp( -1 ); break;          // move down
	}
}

static bool b_leftkey = false;
static int last_x, last_y;

void MouseKey(int button, int state, int x, int y)
{
	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
		b_leftkey = true;
		last_x=x;
		last_y=y;
	}
	if (button == GLUT_RIGHT_BUTTON && state == GLUT_UP) {
		b_leftkey = false;
	}
}

void MouseMove(int x, int y)
{
	// rotate by mouse
	if (b_leftkey) {
		cameraRotateLeft(0.1*(x-last_x));
		cameraRotateUp(0.1*(last_y-y));
	}
	last_x=x;
	last_y=y;
}
